package com.gateway.jug.service;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

import com.gateway.jug.R;
import com.gateway.jug.gps.FuzzControl;
import com.gateway.jug.interfaces.FuzzInfo;
import com.gateway.jug.interfaces.IFuzzInfoCallback;
import com.gateway.jug.interfaces.IRemoteRegistration;
import com.gateway.jug.logic.Haversine;
import com.gateway.jug.logic.Pythagorean;
import com.gateway.jug.logic.Speedometer;

public class FuzzService extends Service {
    private static final String TAG = "FuzzService";

    private LocationManager lm;
    private LocationListener locationListener;
    
    private NotificationManager nm;
    
    private Location previousLocation;
    private long previousTime = -1;

    private double totalPDistance = 0;
	private double totalHDistance = 0;
	
	/**
	 * Note: move to preferences eventually, and reduce to a sane 60 MPH
	 */
	private static final int SPEED_THRESHOLD_CONSTANT = 1000;
	private static final int SPEED_EXCEEDED_MSG = 1;
	private static final String SPEED_DEFAULT_UNITS = "MPH";
	
    final RemoteCallbackList<IFuzzInfoCallback> mCallbacks = new RemoteCallbackList<IFuzzInfoCallback>();
    
	@Override
	public void onCreate() {
		super.onCreate();
        lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);    
        Log.d(TAG, "Obtained Location Manager");
        
	    locationListener = new MyLocationListener();
	    Log.d(TAG, "Created location listener");
	    
	    lm.requestLocationUpdates(
	        LocationManager.GPS_PROVIDER, 
	        0, 
	        0, 
	        locationListener);
	    
	    nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
	    Log.d(TAG, "Created Notification Manager");
	    
		// perform a notification that the service has started.
        Toast.makeText(this, com.gateway.jug.R.string.fuzz_service_start, Toast.LENGTH_SHORT).show();
    }

	@Override
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		super.onStart(intent, startId);
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		// TODO Auto-generated method stub
		super.onConfigurationChanged(newConfig);
	}

	@Override
	public void onDestroy() {
		// unregister all callbacks
		mCallbacks.kill();
		
		// perform a notification that the service has started.
        Toast.makeText(this, com.gateway.jug.R.string.fuzz_service_stop, Toast.LENGTH_SHORT).show();		
	}

	@Override
	public void onLowMemory() {
		// TODO Auto-generated method stub
		super.onLowMemory();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}
	
	private final IRemoteRegistration.Stub mBinder = new IRemoteRegistration.Stub() {
		
		@Override
		public void clearStatistics() throws RemoteException {
			totalHDistance = 0;
			totalPDistance = 0;
			previousLocation = null;
			previousTime = 0;
		}

		@Override
		public void registerCallback(IFuzzInfoCallback cb)
				throws RemoteException {
			if (cb != null) {
				mCallbacks.register(cb);
			}
		}

		@Override
		public void unregisterCallback(IFuzzInfoCallback cb)
				throws RemoteException {
			if (cb != null) {
				mCallbacks.unregister(cb);
			}
		}
	};

    private class MyLocationListener implements LocationListener 
    {
    	
        @Override
        public void onLocationChanged(Location loc) {
        	FuzzInfo fuzz = new FuzzInfo();
        	double currentspeed = 0;
        	long currentTime = 0;

        	if (loc != null) {                
             	if (previousLocation != null) {
             		fuzz.units = SPEED_DEFAULT_UNITS;
              		currentTime = System.nanoTime();
                	fuzz.pdistance = Pythagorean.computeDistance(previousLocation.getLatitude(), 
		                                                    previousLocation.getLongitude(), 
		                                                    loc.getLatitude(), 
		                                                    loc.getLongitude());
                	fuzz.pspeed = Speedometer.computeMPH(fuzz.pdistance, currentTime - previousTime);
                	totalPDistance = totalPDistance + fuzz.pdistance;
                	fuzz.totalPDistance = totalPDistance;
                	Log.d(TAG, "Pythagorean Distance: " + fuzz.pdistance + 
                			" Speed: " + fuzz.pspeed + 
                			" Total Distance: " + fuzz.totalPDistance);
                	
                	if (fuzz.pspeed > SPEED_THRESHOLD_CONSTANT) {
                		nm.notify(SPEED_EXCEEDED_MSG, createSpeedExceededNotification(fuzz.pspeed, fuzz.units));
                	}
                	
                	fuzz.hdistance = Haversine.computeDistance(previousLocation.getLatitude(), 
                			                              previousLocation.getLongitude(), 
                			                              loc.getLatitude(), 
                			                              loc.getLongitude());
                	fuzz.hspeed = Speedometer.computeMPH(fuzz.hdistance, currentTime - previousTime);
                	totalHDistance = totalHDistance + fuzz.hdistance;
                	fuzz.totalHDistance = totalHDistance;
                	Log.d(TAG, "Haversine Distance: " + fuzz.hdistance + 
                			" Speed: " + fuzz.hspeed + 
                			" Total Distance: " + totalHDistance);

                    fuzz.currentlat = loc.getLatitude();
                    fuzz.currentlong = loc.getLongitude();

                    // Broadcast to all clients the new value.
                    final int N = mCallbacks.beginBroadcast();
                    for (int i=0; i<N; i++) {
                        try {
                            mCallbacks.getBroadcastItem(i).updateInfo(fuzz);
                        } catch (RemoteException e) {
                            // The RemoteCallbackList will take care of removing
                            // the dead object for us.
                        }
                    }
                    mCallbacks.finishBroadcast();
             	}

             	previousLocation = loc;
                previousTime = System.nanoTime();
            }
        }

        @Override
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onStatusChanged(String provider, int status, 
            Bundle extras) {
            // TODO Auto-generated method stub
        }
    }    

    private Notification createSpeedExceededNotification(double speed, String units) {
    	Notification result = new Notification(R.drawable.police_32, 
    			             "Speed Exceeded [current speed: " + speed + " " + units + "]",
    			             System.currentTimeMillis());
    	
    		PendingIntent contentIntent = PendingIntent.getActivity(this, SPEED_EXCEEDED_MSG, new Intent(this, FuzzControl.class), 0);

    		result.setLatestEventInfo(this, "Speed Exceeded", "Speed Exceeded [current speed: " + speed + " " + units + "]", contentIntent);

    	return result; 
    }
}
